home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
public
/
RTF
/
view.C
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
12KB
|
651 lines
/* $Header: /usr/people/pcd/Src/RTF/RCS/view.C,v 1.1 92/11/23 12:58:56 pcd Exp Locker: pcd $
*/
#include "view.h"
#include <assert.h>
#include <string.h>
#include "debug.h"
int debug_rtf = 0;
int debug_rtfall = 0;
class RTFParagraphFormat : public ParagraphFormat{
public:
void set_attr(int, int);
Inches li(int twips)
{ return left_indent = (double)twips / 1440; };
Inches ri(int twips)
{ return right_indent = (double)twips / 1440; };
Inches fi(int twips)
{ return first_indent = (double)twips / 1440; };
Inches sb(int twips)
{ return space_before = (double)twips / 1440; };
Inches sa(int twips)
{ return space_after = (double)twips / 1440; };
Inches sl(int twips)
{ return space_between = (double)twips / 1440; };
static void deftab(int twips)
{ default_tab_width((double)twips / 1440); };
void tx(int twips)
{ add_tab_stop((double)twips / 1440 + left_indent); };
};
void
ParagraphFormat::normal()
{
first_indent = left_indent = right_indent = 0;
space_before = space_after = space_between = 0;
tabqty = 0;
justification = quad_left;
}
void
RTFParagraphFormat::set_attr(int attr, int val)
{
switch(attr){
case RTF::ParDef:
normal();
break;
case RTF::QuadLeft:
justification = quad_left;
break;
case RTF::QuadRight:
justification = quad_right;
break;
case RTF::QuadJust:
justification = quad_full;
break;
case RTF::QuadCenter:
justification = quad_center;
break;
case RTF::CellPos: /* @@ table hack */
case RTF::TabPos:
tx(val);
break;
case RTF::SpaceBefore:
sb(val);
break;
case RTF::SpaceAfter:
sa(val);
break;
case RTF::SpaceBetween:
sl(val);
break;
case RTF::LeftIndent:
li(val);
break;
case RTF::RightIndent:
ri(val);
break;
case RTF::FirstIndent:
fi(val);
break;
//@@ others!
}
}
void
RTFCharacterFormat::set_attr(int attr, int val)
{
switch(attr){
case RTF::Plain:
plain();
break;
case RTF::Bold:
bold = val;
break;
case RTF::Italic:
italic = val;
break;
case RTF::Underline:
/* Microsoft RTF never uses \ul0
but I'm supporting it because NeXT rtf files do.
*/
underline = val;
break;
case RTF::NoUnderline:
underline = 0;
break;
case RTF::FontFamily:
font_family = val;
break;
case RTF::SuperScript:
super = val;
break;
case RTF::SubScript:
sub = val;
break;
#ifdef FONT_SIZES
case RTF::FontSize:
size = val;
break;
#endif
case RTF::ForeColor:
foreground = val;
break;
}
}
View::View()
{
flow_ = 0;
page_ = 0;
first_ = last_ = 0;
}
View::~View()
{
delete flow_;
delete page_;
}
int
View::string(const char* d, int rtf)
{
delete page_;
page_ = 0;
delete flow_;
flow_ = 0;
Qty q;
if(d == 0 || (q = strlen(d)) == 0)
return 1;
if(rtf)
flow_ = RichText(d, q);
else
flow_ = ASCII(d, q);
first_ = 0;
last_ = flow_ ? flow_->bytes() : 0;
clear();
return flow_ ? 1 : 0;
}
int
View::format()
{
if(!flow_) return 0; /* must be text available */
if(page_) /* already formatted */
return 1;
page_ = new TextRect();
page_->format(flow_, first(), last(), bounds_);
/* page_->bounds().top() is end of formatted area */
bounds_.height(page_->bounds().top() - bounds_.top());
return 1;
}
int
View::resize(BRect b)
{
if(bounds_ == b)
return 0;
bounds_ = b;
unformat();
return 1;
}
void
View::unformat()
{
if(page_){
delete page_;
page_ = 0;
clear();
}
}
int
View::range(TextPosition f, TextPosition l)
{
if(f == first() && l == last())
return 0;
first_ = f;
last_ = l;
unformat();
return 1;
}
TextFlow*
View::ASCII(const char* data, Qty q)
{
TextFlow* ret = new TextFlow((const Byte*)data, q);
TextPosition first, last;
CharacterFormat plain;
plain.fg_pixel = color(0,0,0); //@@ should be in CharacterFormat::plain()
char* p;
for(first = 0, p=(char*)data;
first < q && (p = strchr(p, '\n'));
p++, first=last+1){
last = p - (char*)data;
if(last>first)
text_flow(*ret, first, last, plain);
new_line(*ret, last, last+1, 0);
}
if(first < q)
text_flow(*ret, first, q, plain);
return ret;
}
class IntTable {
public:
IntTable(IntTable* s, int indx, int elt)
{ link_ = s; indx_ = indx; elt_ = elt; };
~IntTable()
{ delete link_; }
int element(int indx)
// return 0 on not found
{ return indx_ == indx ? elt_ : link_ ? link_->element(indx) : 0;};
private:
int elt_, indx_;
IntTable* link_;
};
class ColorSequence {
public:
ColorSequence(ColorSequence* s, unsigned long p)
{ link_ = s; pixel_ = p; };
~ColorSequence()
{ delete link_; }
ColorSequence* reverse(ColorSequence* prev=0)
{ ColorSequence* l = link_;
link_ = prev;
return l ? l->reverse(this) : this;
};
unsigned long element(int indx)
// return 0 on not found
{ return indx == 0 ? pixel_
: link_ ? link_->element(indx-1) : 0; };
private:
unsigned long pixel_;
ColorSequence* link_;
};
static IntTable*
parse_font_table(RTFPos& here)
{
IntTable* ret = 0;
int fnum = 0; // appease compiler.
int braces = 1;
do{
here.GetToken();
if(here.CheckCM(RTF::Control, RTF::FontFamily))
ret = new IntTable(ret, fnum, here.Minor() - RTF::FFNil);
else if(here.CheckCMM(RTF::Control, RTF::CharAttr, RTF::FontNum))
fnum = here.Param();
else if(here.CheckCM(RTF::Group, RTF::BeginGroup))
braces++;
else if(here.CheckCM(RTF::Group, RTF::EndGroup))
braces--;
}while(braces>0);
return ret;
}
static ColorSequence*
parse_color_table(RTFPos& here, const View& view)
{
ColorSequence* ret = 0;
unsigned char r, g, b;
int braces = 1;
do{
here.GetToken();
if(here.CheckCM(RTF::Control, RTF::ColorName))
switch(here.Minor()){
case RTF::Red:
r = here.Param();
break;
case RTF::Green:
g = here.Param();
break;
case RTF::Blue:
b = here.Param();
ret = new ColorSequence(ret, view.color(r<<8, g<<8, b<<8));
break;
}
else if(here.CheckCM(RTF::Group, RTF::BeginGroup))
braces++;
else if(here.CheckCM(RTF::Group, RTF::EndGroup))
braces--;
}while(braces>0);
return ret ? ret->reverse() : 0;
}
TextFlow*
View::RichText(const char* data, Qty q)
{
assert(data && q>0);
RTFPos token(data);
token.GetToken();
REQUIRE(token.CheckCM(RTF::Group, RTF::BeginGroup), return 0);
TextFlow* parent_group = new TextFlow((const Byte*)data, q);
RTFParagraphFormat::deftab(720);
RTFParagraphFormat pgf_fmt;
RTFCharacterFormat* char_fmt = new RTFCharacterFormat(0);
IntTable* font_table = 0;
ColorSequence* color_table = 0;
TextPosition start_paragraph=0;
int braces = 1;
token.GetToken();
while(braces > 0 && token.start() < q){
Debug(rtfall, ("RTF parse token: '%s'\n", token.TokenText()));
switch(token.Class()){
case RTF::Group:
if(token.Major() == RTF::BeginGroup){
char_fmt = new RTFCharacterFormat(char_fmt);
braces++;
}else{
char_fmt = char_fmt->pop();
braces--;
}
break;
case RTF::Text:
//@@ fg_pixel should be maintained by RTFCharacterFormat
char_fmt->fg_pixel = color_table ?
color_table->element(char_fmt->foreground)
: color(0,0,0);
if(token.Minor()){
text_char(*parent_group, token.start(), token.end(), token.Major(),
*char_fmt);
}else{
parse_text(token, *parent_group, *char_fmt);
continue;
}
break;
case RTF::Control:
switch(token.Major()){
case RTF::DocAttr:
switch(token.Minor()){
case RTF::DefTab:
RTFParagraphFormat::deftab(token.Param());
break;
}
break;
case RTF::CharAttr:
if(token.Minor() == RTF::Invisible)
token.SkipGroup();
else if(token.Minor() == RTF::FontNum){
if(font_table)
// element returns 0, i.e. fnil on errors
char_fmt->set_attr(RTF::FontFamily,
font_table->element(token.Param()));
}
else
char_fmt->set_attr(token.Minor(), token.Param());
break;
case RTF::ParAttr:
pgf_fmt.set_attr(token.Minor(), token.Param());
break;
case RTF::SpecialChar:
switch(token.Minor()){
extern int debug_tabs;
case RTF::Tab:
case RTF::Cell: /* @@ hack to make sense of tables */
new TabFlow(*parent_group, token.start(), token.end(),
pgf_fmt, *this);
break;
case RTF::Line:
case RTF::Row: /* @@ hack to make sense of tables */
new_line(*parent_group, token.start(), token.end(), 0);
break;
case RTF::Page: //@#
case RTF::Par:
//@@ use char format for height of newline
new_line(*parent_group, token.start(), token.end(),
pgf_fmt.space_after);
new ParagraphFlow(*parent_group, start_paragraph, token.end(),
pgf_fmt, *this);
start_paragraph = token.end();
break;
}
break;
case RTF::Destination:
switch(token.Minor()){
case RTF::Field:
case RTF::FieldResult:
case RTF::Index:
break;
case RTF::FontTbl:
font_table = parse_font_table(token);
char_fmt = char_fmt->pop();
braces--;
break;
case RTF::ColorTbl:
color_table = parse_color_table(token, *this);
char_fmt = char_fmt->pop();
braces--;
break;
case RTF::Pict:
parse_picture(token, *parent_group);
/* and skipgroup... */
default:
token.SkipGroup();
char_fmt = char_fmt->pop();
braces--;
break;
}
}
}
token.GetToken();
}
return parent_group;
}
void
RTFPos::getText()
{
size_t len = strcspn(p, "\t\n\r\\{}");
end_+= len;
p+= len;
}
void
RTFPos::getHex()
{
size_t len = strcspn(p, "\\{}");
end_+= len;
p+= len;
}
void
View::parse_text(RTFPos& token, TextFlow& parent, const CharacterFormat& cf)
{
TextPosition start = token.start();
token.getText();
Debug(rtf, ("word(%d,%d)\n", start, token.start()));
text_flow(parent, start, token.end(), cf);
token.GetToken();
}
void
View::parse_picture(RTFPos& token, TextFlow& parent_group)
{
TextPosition start = -1;
Coord height=-1, width=-1;
do{
if(token.CheckCM(RTF::Control, RTF::PictAttr)){
switch(token.Minor()){
case RTF::PicWid:
width = token.Param();
break;
case RTF::PicHt:
height = token.Param();
break;
/* @# others? bitmap type? */
}
}else
if(token.Class() == RTF::Text){
start = token.start();
token.getHex();
}
token.GetToken();
}while(token.Class() != RTF::Group && token.Class() != RTF::Eof);
if(height > 0 && width > 0 && token.start() > start)
/* all color info must be in the hex data.
* right now, SunRaster/RLE (1 or 8 bit) is used
* @@ pass a bitmap type?
*/
picture(parent_group, start, token.start(), width, height);
}
char*
View::plain_text(const char* rich_text, Qty q)
{
RTFPos token(rich_text);
char *ret = new char[q+1];
char *dest = ret;
token.GetToken();
while(token.start() < q){
switch(token.Class()){
case RTF::Group:
break;
case RTF::Text:
*dest++ = token.Major();
break;
case RTF::Control:
switch(token.Major()){
case RTF::CharAttr:
if(token.Minor() == RTF::Invisible)
token.SkipGroup();
break;
case RTF::ParAttr:
break;
case RTF::SpecialChar:
switch(token.Minor()){
case RTF::Tab:
*dest++ = '\t';
break;
case RTF::Line:
*dest++ = '\n';
break;
case RTF::Page:
*dest++ = '\f';
break;
case RTF::Par:
*dest++ = '\n';
*dest++ = '\n';
break;
}
break;
case RTF::Destination:
switch(token.Minor()){
case RTF::Field:
case RTF::FieldResult:
case RTF::Index:
break;
default:
token.SkipGroup();
break;
}
}
}
token.GetToken();
}
*dest++ = 0;
return ret;
}